﻿using System;
using System.Collections;
using System.Text;
using Antlr.Runtime.Tree;
using Antlr.Runtime;
using Evaluant.OPath.Expressions;

namespace Evaluant.OPath
{
    public enum OPathQueryTypeEnum { Path, Expression };

    public class OPathQuery
    {
        private OPathQueryTypeEnum _QueryType;

        protected string expression;
        protected bool compiled = false;
        protected bool hasErrors = false;
        protected string errorMessage = String.Empty;

        protected OPathQuery()
        {
        }

        public OPathQuery(string expression)
        {
            if (expression == null || expression == String.Empty)
                throw new
                    ArgumentException("Expression can't be empty", "expression");

            this.expression = expression;
        }

        public OPathQuery(string expression, OPathQueryTypeEnum queryType)
            : this(expression)
        {
            this._QueryType = queryType;
        }

        public virtual void Compile()
        {
            Compile(true);
        }

        public virtual void Compile(bool optimize)
        {
            if (compiled)
            {
                return;
            }

            compiled = true;

            try
            {
                OPathLexer lexer = new OPathLexer(new ANTLRStringStream(expression));
                OPathParser parser = new OPathParser(new CommonTokenStream(lexer));

                if (_QueryType == OPathQueryTypeEnum.Path)
                {
                    path = parser.pathExpression().value;
                    if (optimize)
                    {
                        OptimizeIndentifierConstraints(path);
                    }
                }
                else
                {
                    call = parser.function().value as Call;
                }
            }
            catch (Exception e)
            {
                hasErrors = true;
                errorMessage = e.Message;
            }

        }

        /// <summary>
        /// Finds constraints with only one identifier inside.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        private void OptimizeIndentifierConstraints(Path path)
        {
            OPathOptimizer optimizer = new OPathOptimizer();
            optimizer.Visit(path);
        }

        public bool HasErrors
        {
            get { return hasErrors; }
        }

        public string ErrorMessage
        {
            get { return errorMessage; }
        }

        protected Path path;

        public virtual Path Path
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return path; }
            internal set
            {
                path = value;
                _QueryType = OPathQueryTypeEnum.Path;
            }
        }

        protected Call call;

        public virtual Call Expression
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return call; }
            internal set
            {
                call = value;
                _QueryType = OPathQueryTypeEnum.Expression;
            }
        }


        /// <summary>
        /// Gets the type of query
        /// </summary>
        public OPathQueryTypeEnum QueryType
        {
            get { return _QueryType; }
            set { _QueryType = value; }
        }
    }
}
